package web

import (
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"net/http"
	"sync"
	"time"
)

// http升级websocket协议的配置
var WsUpgrader = websocket.Upgrader{
	// 允许所有CORS跨域请求
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// 客户端读写消息
type WsMessage struct {
	//消息类型
	MessageType int
	//数据字节数组
	Data []byte
}

// 客户端连接
type WsConnection struct {
	WsSocket *websocket.Conn // 底层websocket
	InChan   chan *WsMessage // 读队列
	OutChan  chan *WsMessage // 写队列

	//并发原语
	Mutex     sync.Mutex // 避免重复关闭管道
	IsClosed  bool       //连接关闭标识位
	CloseChan chan byte  // 关闭通知
}

//读消息
func (wsConn *WsConnection) WsReadLoop() {
	for {
		// 读一个message
		msgType, data, err := wsConn.WsSocket.ReadMessage()
		if err != nil {
			goto error
		}
		//构造消息对象
		req := &WsMessage{
			msgType,
			data,
		}
		// 放入请求队列
		select {
		case wsConn.InChan <- req:
		case <-wsConn.CloseChan:
			goto closed
		}
	}
error:
	wsConn.WsClose()
closed:
}

//写消息
func (wsConn *WsConnection) WsWriteLoop() {
	for {
		select {
		// 取一个应答
		case msg := <-wsConn.OutChan:
			// 写给websocket
			if err := wsConn.WsSocket.WriteMessage(msg.MessageType, msg.Data); err != nil {
				goto error
			}
		case <-wsConn.CloseChan:
			goto closed
		}
	}
error:
	wsConn.WsClose()
closed:
}

func (wsConn *WsConnection) ProcLoop() {

	// 启动一个gouroutine发送心跳
	go func() {
		for {
			time.Sleep(2 * time.Second)
			if err := wsConn.WsWrite(websocket.TextMessage, []byte("{\"result\":\"0000\",\"data\":\"heartbeat from server\"}")); err != nil {
				fmt.Println("heartbeat fail")
				wsConn.WsClose()
				break
			}
		}
	}()

}

func WsHandler(resp http.ResponseWriter, req *http.Request) *WsConnection {

	// 应答客户端告知升级连接为websocket
	wsSocket, err := WsUpgrader.Upgrade(resp, req, nil)
	if err != nil {
		return nil
	}

	wsConn := &WsConnection{
		WsSocket:  wsSocket,
		InChan:    make(chan *WsMessage, 1000),
		OutChan:   make(chan *WsMessage, 1000),
		CloseChan: make(chan byte),
		IsClosed:  false,
	}

	return wsConn

}

func (wsConn *WsConnection) WsWrite(messageType int, data []byte) error {
	select {
	case wsConn.OutChan <- &WsMessage{messageType, data}:
	case <-wsConn.CloseChan:
		return errors.New("websocket closed")
	}
	return nil
}

func (wsConn *WsConnection) WsRead() (*WsMessage, error) {
	select {
	case msg := <-wsConn.InChan:
		return msg, nil
	case <-wsConn.CloseChan:
	}
	return nil, errors.New("websocket closed")
}

func (wsConn *WsConnection) WsClose() {

	//关闭websocket连接
	wsConn.WsSocket.Close()

	//加锁
	wsConn.Mutex.Lock()
	//解锁
	defer wsConn.Mutex.Unlock()
	//判断加锁标识位是否为true
	if !wsConn.IsClosed {
		wsConn.IsClosed = true
		//关闭关闭通道
		close(wsConn.CloseChan)
	}
}
